package tablut;

public abstract class Pion implements Cloneable {
	static final int HAUT = 0;
	static final int DROITE = 1;
	static final int BAS = 2;
	static final int GAUCHE = 3;
	
	int couleur;
	int[] distMax;
	boolean estSurPlateau;
	Case cas;
	Plateau plat;
	
	Pion(int couleur, int haut, int droite, int bas, int gauche, Case cas, Plateau plat) {
		this.couleur = couleur;
		distMax = new int[4];
		distMax[0] = haut;
		distMax[1] = droite;
		distMax[2] = bas;
		distMax[3] = gauche;
		estSurPlateau = true;
		this.cas = cas;
		this.plat = plat;
	}
	
	
	
	// Méthode pour récupérer la couleur de l'ennemi du pion
	public int getCoulEnnemi() {
		return couleur%2 + 1;
	}
	
	// Cette méthode va chercher le premier pion dans la direction en paramètre de manière itérative.
	public Pion getPionParIter(int dir) {
		int i;
		
		switch(dir) {
		case HAUT:	// Recherche vers le haut
			i = cas.y;
			while(--i >= 0 && plat.plateau[cas.x][i].p == null);
			if(i <= -1)
				return null;
			return plat.plateau[cas.x][i].p;
		case DROITE:	// Recherche vers le droite
			i = cas.x;
			while(++i < 9 && plat.plateau[i][cas.y].p == null);
			if(i >= 9)
				return null;
			return plat.plateau[i][cas.y].p;
		case BAS:	// Recherche vers le bas
			i = cas.y;
			while(++i < 9 && plat.plateau[cas.x][i].p == null);
			if(i >= 9)
				return null;
			return plat.plateau[cas.x][i].p;
		case GAUCHE:	// Recherche vers le gauche
			i = cas.x;
			while(--i >= 0 && plat.plateau[i][cas.y].p == null);
			if(i <= -1)
				return null;
			return plat.plateau[i][cas.y].p;
		default:
			return null;
		}
	}
	
	// Cette méthode va calculer la position du pion dans la direction en paramètre à partir du tableau des distances.
	public Pion getPionParCalc(int dir) {
		int i;
		
		switch(dir) {
		case HAUT:	// Calcul du pion voisin vers le haut
			i = cas.y - distMax[HAUT] - 1;
			if(i <= -1)
				return null;
			return plat.plateau[cas.x][i].p;
		case DROITE:	// Calcul du pion voisin vers la droite
			i = cas.x + distMax[DROITE] + 1;
			if(i >= 9)
				return null;
			return plat.plateau[i][cas.y].p;
		case BAS:	// Calcul du pion voisin vers le bas
			i = cas.y + distMax[BAS] + 1;
			if(i >= 9)
				return null;
			return plat.plateau[cas.x][i].p;
		case GAUCHE:	// Calcul du pion voisin vers la gauche
			i = cas.x - distMax[GAUCHE] - 1;
			if(i <= -1)
				return null;
			return plat.plateau[i][cas.y].p;
		default:
			return null;
		}
	}
	
	// Méthode "reliant" (en mettant à jour les distances) les deux pions voisins de celui courant en ligne ou en colonne.
	public void relierVoisins(boolean horizontal) {
		int nouvDist;
		Pion tmpPion;
		
		if(horizontal) {	// Liaison des pions voisins horizontaux
			// La nouvelle distance = la distance à gauche + celle à droite + 1 car le pion va bouger, libérant une case
			nouvDist = distMax[GAUCHE] + distMax[DROITE] + 1;
			
			tmpPion = getPionParCalc(GAUCHE);
			if(tmpPion != null)	// S'il y a un pion voisin vers la gauche
				tmpPion.distMax[DROITE] = nouvDist;
			
			tmpPion = getPionParCalc(DROITE);
			if(tmpPion != null)	// S'il y a un pion voisin vers la droite
				tmpPion.distMax[GAUCHE] = nouvDist;
		}
		else {	// Liaison des pions voisins verticaux
			nouvDist = distMax[HAUT] + distMax[BAS] + 1;
			
			tmpPion = getPionParCalc(HAUT);
			if(tmpPion != null)	// S'il y a un pion voisin vers le haut
				tmpPion.distMax[BAS] = nouvDist;
			
			tmpPion = getPionParCalc(BAS);
			if(tmpPion != null)	// S'il y a un pion voisin vers le bas
				tmpPion.distMax[HAUT] = nouvDist;
		}
	}
	
	/* La méthode "insère" le pion entre les pions voisins se trouvant horizontalement ou veticalement.
	 * C'est l'inverse de relier.
	 * Méthode version historique.
	 */
	public void sInsererAvecHisto(boolean horizontal, Historique histo) {
		Pion vois1, vois2;
		
		if(horizontal) {	// En ligne
			vois1 = getPionParIter(GAUCHE);
			vois2 = getPionParIter(DROITE);
			
			if(vois1 == null) {	// S'il n'y a pas de voisin vers la gauche
				if(vois2 == null) {	// S'il n'y a pas de voisin vers la droite non plus
					// On recalcule les distances
					distMax[GAUCHE] = cas.x;
					distMax[DROITE] = 8 - cas.x;
				}
				else {	// S'il n'y a pas de voisin vers la droite tout de même
					histo.ajouterElemHisto(vois2);
					
					// On recalcule les distances
					distMax[DROITE] = vois2.cas.x - cas.x - 1;
					distMax[GAUCHE] = cas.x;
					vois2.distMax[GAUCHE] = distMax[DROITE];
				}
			}
			else {	// S'il y a un voisin vers la gauche
				histo.ajouterElemHisto(vois1);
				
				if(vois2 == null) {	// Mais pas de voisin à droite
					// On recalcule les distances
					distMax[GAUCHE] = cas.x - vois1.cas.x - 1;
					distMax[DROITE] = 8 - cas.x;
					vois1.distMax[DROITE] = distMax[GAUCHE];
				}
				else {	// Et un voisin vers la droite
					histo.ajouterElemHisto(vois2);
					
					// On recalcule les distances
					distMax[GAUCHE] = cas.x - vois1.cas.x - 1;
					distMax[DROITE] = vois2.cas.x - cas.x - 1;
					vois1.distMax[DROITE] = distMax[GAUCHE];
					vois2.distMax[GAUCHE] = distMax[DROITE];
				}
			}
		}
		else {	// En colonne
			vois1 = getPionParIter(HAUT);
			vois2 = getPionParIter(BAS);
			
			if(vois1 == null) {	// Il n'y a pas de voisin en haut
				if(vois2 == null) {	// ni en bas
					// On recalcule les distances
					distMax[HAUT] = cas.y;
					distMax[BAS] = 8 - cas.y;
				}
				else {	// mais en bas, oui !
					histo.ajouterElemHisto(vois2);
					
					// On recalcule les distances
					distMax[BAS] = vois2.cas.y - cas.y - 1;
					distMax[HAUT] = cas.y;
					vois2.distMax[HAUT] = distMax[BAS];
				}
			}
			else {
				histo.ajouterElemHisto(vois1);
				
				if(vois2 == null) {
					// On recalcule les distances
					distMax[HAUT] = cas.y - vois1.cas.y - 1;
					distMax[BAS] = 8 - cas.y;
					vois1.distMax[BAS] = distMax[HAUT];
				}
				else {
					histo.ajouterElemHisto(vois2);
					
					// On recalcule les distances
					distMax[HAUT] = cas.y - vois1.cas.y - 1;
					distMax[BAS] = vois2.cas.y - cas.y - 1;
					vois1.distMax[BAS] = distMax[HAUT];
					vois2.distMax[HAUT] = distMax[BAS];
				}
			}
		}
	}
	
	/* La méthode "insère" le pion entre les pions voisins se trouvant horizontalement ou veticalement.
	 * C'est l'inverse de relier.
	 * Méthode version sans historique.
	 */
	public void sInsererSansHisto(boolean horizontal) {
		Pion vois1, vois2;
		
		if(horizontal) {	// En ligne
			vois1 = getPionParIter(GAUCHE);
			vois2 = getPionParIter(DROITE);
			
			if(vois1 == null) {	// S'il n'y a pas de voisin vers la gauche
				if(vois2 == null) {	// S'il n'y a pas de voisin vers la droite non plus
					distMax[GAUCHE] = cas.x;
					distMax[DROITE] = 8 - cas.x;
				}
				else {	// S'il n'y a pas de voisin vers la droite tout de même
					// On recalcule les distances
					distMax[DROITE] = vois2.cas.x - cas.x - 1;
					distMax[GAUCHE] = cas.x;
					vois2.distMax[GAUCHE] = distMax[DROITE];
				}
			}
			else {	// S'il y a un voisin vers la gauche
				if(vois2 == null) {	// Mais pas de voisin à droite
					// On recalcule les distances
					distMax[GAUCHE] = cas.x - vois1.cas.x - 1;
					distMax[DROITE] = 8 - cas.x;
					vois1.distMax[DROITE] = distMax[GAUCHE];
				}
				else {	// Et un voisin vers la droite
					distMax[GAUCHE] = cas.x - vois1.cas.x - 1;
					distMax[DROITE] = vois2.cas.x - cas.x - 1;
					vois1.distMax[DROITE] = distMax[GAUCHE];
					vois2.distMax[GAUCHE] = distMax[DROITE];
				}
			}
		}
		else {	// En colonne
			vois1 = getPionParIter(HAUT);
			vois2 = getPionParIter(BAS);
			
			if(vois1 == null) {	// Il n'y a pas de voisin en haut
				if(vois2 == null) {	// ni en bas
					distMax[HAUT] = cas.y;
					distMax[BAS] = 8 - cas.y;
				}
				else {	// mais en bas, oui !
					// On recalcule les distances
					distMax[BAS] = vois2.cas.y - cas.y - 1;
					distMax[HAUT] = cas.y;
					vois2.distMax[HAUT] = distMax[BAS];
				}
			}
			else {
				if(vois2 == null) {
					// On recalcule les distances
					distMax[HAUT] = cas.y - vois1.cas.y - 1;
					distMax[BAS] = 8 - cas.y;
					vois1.distMax[BAS] = distMax[HAUT];
				}
				else {
					// On recalcule les distances
					distMax[HAUT] = cas.y - vois1.cas.y - 1;
					distMax[BAS] = vois2.cas.y - cas.y - 1;
					vois1.distMax[BAS] = distMax[HAUT];
					vois2.distMax[HAUT] = distMax[BAS];
				}
			}
		}
	}
	
	// Les méthodes abstraites
	public abstract boolean estEncercle(int dejaVerif);
	@Override
	public abstract Pion clone();	// Nécessaire pour l'historique.
	public abstract void reprendreEtat(Pion pion);
}
